package org.dreamwork.assistant.tools.network.gui;

import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.intellij.uiDesigner.core.Spacer;
import org.dreamwork.assistant.tools.network.gui.SetupForm.OnSelectListener;
import org.dreamwork.ui.concurrent.Handler;
import org.dreamwork.ui.concurrent.IMessageHandler;
import org.dreamwork.ui.concurrent.Message;
import org.dreamwork.util.IOUtil;
import org.dreamwork.util.StringUtil;
import org.dreamwork.util.Tools;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.*;
import java.net.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * Created by seth.yang on 2018/5/7
 */
public class MainFrame extends JFrame
        implements ActionListener, IMessageHandler, OnSelectListener,
        NetworkReader.NetworkReaderListener, TcpClientWorker.TcpClientListener {
    private static final int TCP = 0, UDP = 1;
    private static final int CLIENT = 0, SERVER = 1;

    private JRadioButton rbTCP;
    private JRadioButton rbUDP;
    private JRadioButton rbServer;
    private JRadioButton rbClient;
    private JTextField txtRemote;
    private JButton btnOpen;
    private JLabel lblTarget;
    private JPanel root;
    private JCheckBox cbRedirect;
    private JCheckBox cbReturn;
    private JCheckBox cbShowTime;
    private JCheckBox cbShowHex;
    private JCheckBox cbPauseReceive;
    private JButton btnClear;
    private JButton btnSave;
    private JButton cbSendFile;
    private JCheckBox btnAppend;
    private JCheckBox cbSendHex;
    private JCheckBox cbAutoSend;
    private JTextField txtInterval;
    private JTextPane txtReceive;
    private JButton btnSend;
    private JTextArea txtSend;
    private JComboBox<String> cbClients;
    private JButton btnClose;
    private JPanel pClientList;
    private JCheckBox cbEcho;
    private JLabel lblLink;

    private CircleIcon icon;

    private int protocol = TCP, role = CLIENT;
    private JFileChooser chooser;
    private boolean connected;
    private Handler handler;
    private INetworkAdapter adapter;
    private IMessageLogger logger;
    private ExecutorService executor;
    private NetworkReader reader;
    private MessageSendWorker worker;
    private Config config;
    private ServerSocket server;
    private Map<String, TcpClientWorker> clients = new ConcurrentHashMap<> ();
    private Future<?> future;

    private static final int WIDTH = 960, HEIGHT = 650;
    private static final String GIT_ADDRESS = "https://gitee.com/seth_yang/dreamwork/tree/master/modules/assistant";

    MainFrame () {
        super ("网络调试助手");
        handler = new Handler (this);
        executor = Executors.newCachedThreadPool ();

        guiSetup ();
        setVisible (true);

        init ();

        setDefaultCloseOperation (WindowConstants.EXIT_ON_CLOSE);
    }

    private void guiSetup () {
        setSize (WIDTH, HEIGHT);
        setMinimumSize (new Dimension (WIDTH, HEIGHT));
        Dimension D = Toolkit.getDefaultToolkit ().getScreenSize ();
        int x = (D.width - WIDTH) / 2, y = (D.height - HEIGHT) / 2;
        setLocation (x, y);

        icon = new CircleIcon ();
        btnOpen.setIcon (icon);

        rbTCP.setSelected (true);
        rbClient.setSelected (true);

        URL url = getClass ().getClassLoader ().getResource ("network.png");
        ImageIcon icon = new ImageIcon (url, null);
        setIconImage (icon.getImage ());

        ButtonGroup group = new ButtonGroup ();
        group.add (rbTCP);
        group.add (rbUDP);

        group = new ButtonGroup ();
        group.add (rbServer);
        group.add (rbClient);
        setContentPane (root);

        cbShowTime.setSelected (true);
        cbShowHex.setSelected (true);
        cbReturn.setSelected (true);

        btnSend.setEnabled (false);

        chooser = new JFileChooser ();

        logger = new TextAreaLogger (txtReceive, true, false);
        txtReceive.setEditable (false);

        cbClients.addItem ("所有客户端");

        // network settings
        rbClient.addActionListener (this);
        rbServer.addActionListener (this);
        rbTCP.addActionListener (this);
        rbUDP.addActionListener (this);
        btnOpen.addActionListener (this);

        // receiver settings
        cbRedirect.addActionListener (this);
        cbReturn.addActionListener (this);
        cbShowTime.addActionListener (this);
        cbShowHex.addActionListener (this);
        cbPauseReceive.addActionListener (this);
        btnClear.addActionListener (this);
        btnSave.addActionListener (this);

        // sender settings
        cbSendHex.addActionListener (this);
        btnAppend.addActionListener (this);
        cbAutoSend.addActionListener (this);
        cbSendFile.addActionListener (this);

        // send panel
        btnClose.addActionListener (this);
        btnSend.addActionListener (this);

        pClientList.setVisible (false);
        final Color color = lblLink.getForeground ();
        lblLink.addMouseListener (new MouseAdapter () {
            @Override
            public void mouseClicked (MouseEvent e) {
                try {
                    URL url = new URL (GIT_ADDRESS);
                    URI uri = url.toURI ();
                    Desktop.getDesktop ().browse (uri);
                } catch (Exception ex) {
                    ex.printStackTrace ();
                }
            }

            @Override
            public void mouseEntered (MouseEvent e) {
                lblLink.setCursor (Cursor.getPredefinedCursor (Cursor.HAND_CURSOR));
                lblLink.setText ("<html><u>" + GIT_ADDRESS + "</u></html>");
                lblLink.setForeground (Color.RED);
            }

            @Override
            public void mouseExited (MouseEvent e) {
                lblLink.setCursor (Cursor.getDefaultCursor ());
                lblLink.setText (GIT_ADDRESS);
                lblLink.setForeground (color);
            }
        });
    }

    private void init () {
        worker = new MessageSendWorker ();
        worker.pause = true;

        executor.execute (worker);
        executor.execute ((Runnable) logger);
    }

    @Override
    public void actionPerformed (final ActionEvent e) {
        Object sender = e.getSource ();
        if (sender == rbClient) {
            role = CLIENT;
            lblTarget.setText ("远程地址和端口");
            cbEcho.setEnabled (false);
            cbSendFile.setEnabled (true);
        } else if (sender == rbServer) {
            role = SERVER;
            lblTarget.setText ("本地端口");
            cbEcho.setEnabled (true);
            cbSendFile.setEnabled (false);
        } else if (sender == rbTCP) {
            protocol = TCP;
        } else if (sender == rbUDP) {
            protocol = UDP;
        } else if (sender == cbShowHex) {
            logger.setShowAscii (!cbShowHex.isSelected ());
        } else if (sender == cbShowTime) {
            logger.setShowTime (cbShowTime.isSelected ());
        } else if (sender == cbReturn) {
            logger.setAutoReturn (cbReturn.isSelected ());
        } else if (sender == cbRedirect) {
            if (cbRedirect.isSelected ()) {
                redirectToFile ();
            } else {
                logger.dumpTo (null);
            }
        } else if (sender == cbSendFile) {
            sendFile ();
        } else if (sender == btnClear) {
            txtReceive.setText (null);
        } else if (sender == btnSave) {
            saveData ();
        } else if (sender == btnAppend) {
            if (btnAppend.isSelected ()) {
                SetupForm form = new SetupForm (this, "设置");
                form.setOnSelectListener (this);
                form.setVisible (true);
            } else {
                config = null;
            }
        } else if (sender == cbAutoSend) {
            if (cbAutoSend.isSelected ()) {
                btnSend.setText ("开始发送");
            } else {
                worker.pause = true;
                btnSend.setText ("停止发送");
            }
        } else if (sender == btnOpen) {
            if (connected) {
                disconnect ();
            } else {
                handler.sendNonUIMessage (CONNECT);
            }
        } else if (sender == btnSend) {
            if (connected) {
                handler.sendNonUIMessage (SEND_DATA);
            }
        } else if (sender == btnClose) {
            closeClient ();
        }
    }

    private void closeClient () {
        String name = (String) cbClients.getSelectedItem ();
        if (!clients.containsKey (name)) {
            showError ("请选择要关闭的客户端");
            return;
        }

        TcpClientWorker worker = clients.get (name);
        try {
            worker.close ();
        } catch (IOException e) {
            e.printStackTrace ();
        } finally {
            clients.remove (name);
        }
    }

    private void closeAllClients () {
        for (String name : clients.keySet ()) {
            TcpClientWorker worker = clients.get (name);
            try {
                worker.close ();
            } catch (IOException ex) {
                ex.printStackTrace ();
            }
        }

        clients.clear ();
        cbClients.removeAllItems ();
        cbClients.addItem ("所有客户端");
    }

    private void redirectToFile () {
        chooser.showSaveDialog (this);
        File file = chooser.getSelectedFile ();
        if (file == null)
            return;

        if (!checkParent (file)) {
            return;
        }

        try {
            logger.dumpTo (new FileOutputStream (file));
        } catch (FileNotFoundException ex) {
            showError (ex.getMessage ());
            logger.append (ex.getMessage ().getBytes (), IMessageLogger.ERROR);
        }
    }

    private boolean checkParent (File file) {
        File parent = file.getParentFile ();
        if (!parent.exists () && !parent.mkdirs ()) {
            try {
                showError ("不能创建文件" + file.getCanonicalPath ());
            } catch (IOException e) {
                e.printStackTrace ();
            }
            return false;
        }
        return true;
    }

    private void saveData () {
        if (chooser.showSaveDialog (this) == JFileChooser.APPROVE_OPTION) {
            File file = chooser.getSelectedFile ();
            if (file != null) {
                if (!checkParent (file)) {
                    return;
                }

                handler.sendNonUIMessage (SAVE_DATA, file);
            }
        }
    }

    private void saveData (File file) {
        try (OutputStream out = new FileOutputStream (file)) {
            String text = txtReceive.getText ();
            out.write (text.getBytes ());
            out.flush ();

            showMessage ("保存成功");
        } catch (IOException ex) {
            showError ("保存失败");
        }
    }

    private void sendFile () {
        if (!isConnected ()) {  // not connect
            return;
        }

        if (chooser.showOpenDialog (this) == JFileChooser.APPROVE_OPTION) {
            File file = chooser.getSelectedFile ();
            if (file != null) {
                handler.sendNonUIMessage (SEND_FILE, file);
            }
        }
    }

    private void sendFile (File file) {
        long size = file.length ();
        if (size > (1 << 20)) { // the file length more than 1M
            showError ("不能发送超过1M的文件");
            return;
        }

        try {
            byte[] buff = IOUtil.read (file);
            if (adapter != null) {
                if (cbSendHex.isSelected ()) {
                    String text = new String (buff);
                    buff = Utils.toHex (text);
                }

                adapter.write (buff);
            }
        } catch (IOException e) {
            e.printStackTrace ();
        }
    }

    private boolean isConnected () {
        return connected;
    }

    private void connect () {
        try {
            if (protocol == TCP) {
                if (role == CLIENT) {
                    try {
                        if (connectToTcp ()) {
                            setUIState (true);
                        }
                    } catch (Exception ex) {
                        showError (ex.getMessage ());
                    }
                } else {
                    try {
                        createTcpServer ();
                        connected = true;
                        icon.setState (1);
                        btnOpen.setText ("断开连接");
                        btnSend.setEnabled (true);
                        pClientList.setVisible (true);
                    } catch (NumberFormatException ex) {
                        showError ("无效的端口号，请输入 1~65534的整数");
                    } catch (Exception ex) {
                        showError (ex.getMessage ());
                    }
                }
            } else {
                if (role == CLIENT) {
                    connectToUdp ();
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace ();
        }
    }

    private void createTcpServer () throws Exception {
        String s_port = txtRemote.getText ();
        int port = Integer.parseInt (s_port);
        if (port <= 0 || port >= 65535) {
            throw new NumberFormatException ();
        }
        server = new ServerSocket (port);
        logger.append (("TCP Server 监听在" + server.getLocalSocketAddress ()).getBytes (), IMessageLogger.INFO);
        future = executor.submit (new Runnable () {
            @Override
            public void run () {
                Thread thread = Thread.currentThread ();
                thread.setName ("TCPServer");
                while (!thread.isInterrupted ())
                    try {
                        Socket socket = server.accept ();
                        new TcpClientWorker (socket, /*logger, */executor, MainFrame.this);
                        logger.append (("收到客户端" + socket.getRemoteSocketAddress () + "连接").getBytes (), IMessageLogger.INFO);
                    } catch (Exception ex) {
                        logger.append (ex.getMessage ().getBytes (), IMessageLogger.ERROR);
                    }
            }
        });
    }

    private void disconnect () {
        connected = false;
        icon.setState (0);
        btnOpen.setText ("打开连接");
        if (adapter != null) {
            INetworkAdapter tmp = adapter;
            adapter = null;
            try {
                tmp.close ();
            } catch (IOException ex) {
                ex.printStackTrace ();
            }
        }
        worker.pause = true;
        btnSend.setEnabled (false);
        if (reader != null) {
            reader.shutdown ();
        }
        reader = null;

        if (protocol == TCP && role == SERVER) {
            pClientList.setVisible (false);
        }

        if (future != null)
            future.cancel (true);

        closeAllClients ();

        if (server != null) try {
            server.close ();
        } catch (IOException ex) {
            ex.printStackTrace ();
        }

        setUIState (false);
    }

    private boolean connectToTcp () throws IOException {
        Host host = getHostInfo ();
        if (host != null) {
            Socket socket = new Socket (host.host, host.port);
            adapter = new TcpAdapter (socket);
            reader = new TcpReader (socket.getInputStream ()/*, logger*/);
            reader.setListener (this);
            executor.execute (reader);
            icon.setState (1);
            connected = true;
            btnOpen.setText ("关闭连接");

            worker.adapter = adapter;
            btnSend.setEnabled (true);

            return true;
        } else {
            return false;
        }
    }

    private void connectToUdp () throws IOException {
        Host host = getHostInfo ();
        if (host != null) {
            byte[] buff = new byte[10240];
            InetAddress address = InetAddress.getByName (host.host);
            DatagramSocket socket = new DatagramSocket ();
            DatagramPacket packet = new DatagramPacket (buff, buff.length, address, host.port);
            adapter = new UdpAdapter (socket, packet);

            reader = new UdpReader (socket, packet/*, logger*/);
            reader.setListener (this);
            executor.execute (reader);
            icon.setState (1);
            connected = true;
            btnOpen.setText ("关闭连接");

            worker.adapter = adapter;
            btnSend.setEnabled (true);
        }
    }

    private void showError (String message) {
        handler.sendUIMessage (SHOW_ERROR, message);
    }

    private void showMessage (String message) {
        handler.sendUIMessage (SHOW_MSG, message);
    }

    @Override
    public void handleUIMessage (Message message) {
        switch (message.what) {
            case SHOW_ERROR:
                JOptionPane.showMessageDialog (this, message.obj, "错误", JOptionPane.ERROR_MESSAGE);
                break;
            case SHOW_MSG:
                JOptionPane.showMessageDialog (this, message.obj, "信息", JOptionPane.INFORMATION_MESSAGE);
                break;
        }
    }

    @Override
    public void handleNonUIMessage (Message message) {
        try {
            switch (message.what) {
                case CONNECT:
                    connect ();
                    break;
                case SEND_DATA:
                    sendData ();
                    break;
                case SEND_FILE:
                    sendFile ((File) message.obj);
                    break;
                case SAVE_DATA:
                    saveData ((File) message.obj);
                    break;
            }
        } catch (Exception ex) {
            ex.printStackTrace ();
            showError (ex.getMessage ());
            logger.append (ex.getMessage ().getBytes (), IMessageLogger.ERROR);
        }
    }

    private Host getHostInfo () {
        String text = txtRemote.getText ();
        int pos = text.lastIndexOf (":");
        if (pos < 0) {
            showError ("请设置远程地址和端口");
            return null;
        }

        String host = text.substring (0, pos);
        String s_port = text.substring (pos + 1);
        int port;
        try {
            port = Integer.parseInt (s_port);
        } catch (Exception ex) {
            showError ("请设置远程地址和端口");
            ex.printStackTrace ();
            return null;
        }

        Host h = new Host ();
        h.host = host;
        h.port = port;
        return h;
    }

    private void sendData () throws IOException {
        if (!worker.pause) {
            worker.pause = true;
            btnSend.setText ("开始发送");
        } else {
            String text = txtSend.getText ();
            if (StringUtil.isEmpty (text)) {
                return;
            }

            byte[] buff;
            if (cbSendHex.isSelected ()) {
                buff = Utils.toHex (text);
                if (config != null) {
                    buff = appendData (buff);
                }
            } else {
                buff = text.getBytes ();
            }

            if (adapter != null) {
                if (cbAutoSend.isSelected ()) {
                    worker.adapter = adapter;
                    worker.data = buff;
                    try {
                        worker.interval = Integer.parseInt (txtInterval.getText ());
                    } catch (Exception ex) {
                        // ignore
                    }
                    worker.resume ();
                    btnSend.setText ("停止发送");
                } else {
                    adapter.write (buff);
                }
            } else if (role == SERVER && protocol == TCP) {
                String name = (String) cbClients.getSelectedItem ();
                if (clients.containsKey (name)) {
                    TcpClientWorker worker = clients.get (name);
                    worker.write (buff);
                } else for (TcpClientWorker worker : clients.values ()) {
                    try {
                        worker.write (buff);
                    } catch (IOException ex) {
                        logger.append (("Can't write data to " + worker.name).getBytes (), IMessageLogger.ERROR);
                    }
                }
            }
        }
    }

    private byte[] appendData (byte[] data) throws IOException {
        if (config == null)
            return data;

        if (config.head == null && config.tail == null && config.type == null) {
            return data;
        }

        ByteArrayOutputStream baos = new ByteArrayOutputStream ();
        if (config.head != null && config.head.length > 0) {
            baos.write (config.head);
        }
        baos.write (data);

        if (config.type != null) switch (config.type) {
            case NSum8:
            case Sum8: {
                int sum;
                if (config.includeHead && config.head != null) {
                    sum = Tools.checksum8 (baos.toByteArray ());
                } else {
                    sum = Tools.checksum8 (data);
                }
                baos.write (config.type == CRCType.Sum8 ? sum : -sum);
            }
            break;
            case XOR8: {
                int sum;
                if (config.includeHead && config.head != null) {
                    sum = Tools.xor (baos.toByteArray ());
                } else {
                    sum = Tools.xor (data);
                }
                baos.write (sum);
            }
            break;
            case CRC16: {
                byte[] sum;
                if (config.includeHead && config.head != null) {
                    sum = Tools.crc16 (baos.toByteArray ());
                } else {
                    sum = Tools.crc16 (data);
                }
                baos.write (sum);
            }
            break;
        }

        if (config.tail != null && config.tail.length > 0) {
            baos.write (config.tail);
        }

        return baos.toByteArray ();
    }

    @Override
    public void accept (Config config) {
        this.config = config;
        if (config == null) {
            btnAppend.setSelected (false);
        }
    }

    @Override
    public void onClosed () {
        disconnect ();
        logger.append ("对端断开连接".getBytes (), IMessageLogger.INFO);
    }

    @Override
    public void onReceived (byte[] data) {
        logger.append (data);
        if (cbEcho.isSelected ()) {
            if (adapter != null) {
                try {
                    adapter.write (data);
                } catch (IOException e) {
                    e.printStackTrace ();
                }
            } else if (!clients.isEmpty ()) {
                for (TcpClientWorker worker : clients.values ()) {
                    try {
                        worker.write (data);
                    } catch (IOException e) {
                        e.printStackTrace ();
                    }
                }
            }
        }
    }

    private void setUIState (boolean locked) {
        rbUDP.setEnabled (!locked);
        rbTCP.setEnabled (!locked);
        rbServer.setEnabled (!locked);
        rbClient.setEnabled (!locked);
        txtRemote.setEnabled (!locked);
    }

    @Override
    public void onConnected (TcpClientWorker worker) {
        cbClients.addItem (worker.name);
        clients.put (worker.name, worker);
    }

    @Override
    public void onDisconnected (TcpClientWorker worker) {
        cbClients.removeItem (worker.name);
        clients.remove (worker.name);
        logger.append ((worker.name + "断开连接").getBytes (), IMessageLogger.INFO);
    }

    {
// GUI initializer generated by IntelliJ IDEA GUI Designer
// >>> IMPORTANT!! <<<
// DO NOT EDIT OR ADD ANY CODE HERE!
        $$$setupUI$$$ ();
    }

    /**
     * Method generated by IntelliJ IDEA GUI Designer
     * >>> IMPORTANT!! <<<
     * DO NOT edit this method OR call it in your code!
     *
     * @noinspection ALL
     */
    private void $$$setupUI$$$ () {
        root = new JPanel ();
        root.setLayout (new GridLayoutManager (7, 3, new Insets (0, 0, 0, 0), -1, -1));
        root.setBorder (BorderFactory.createTitledBorder (BorderFactory.createEmptyBorder (5, 5, 5, 5), null));
        final JPanel panel1 = new JPanel ();
        panel1.setLayout (new GridLayoutManager (7, 2, new Insets (0, 0, 0, 0), -1, -1));
        root.add (panel1, new GridConstraints (0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
        panel1.setBorder (BorderFactory.createTitledBorder (BorderFactory.createEtchedBorder (), "网络设置"));
        final JLabel label1 = new JLabel ();
        label1.setText ("协议");
        panel1.add (label1, new GridConstraints (0, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        rbTCP = new JRadioButton ();
        rbTCP.setText ("TCP");
        panel1.add (rbTCP, new GridConstraints (1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        rbUDP = new JRadioButton ();
        rbUDP.setText ("UDP");
        panel1.add (rbUDP, new GridConstraints (1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final JLabel label2 = new JLabel ();
        label2.setText ("角色");
        panel1.add (label2, new GridConstraints (2, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        rbServer = new JRadioButton ();
        rbServer.setText ("服务端");
        panel1.add (rbServer, new GridConstraints (3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        rbClient = new JRadioButton ();
        rbClient.setText ("客户端");
        panel1.add (rbClient, new GridConstraints (3, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        lblTarget = new JLabel ();
        lblTarget.setText ("远程地址");
        panel1.add (lblTarget, new GridConstraints (4, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        txtRemote = new JTextField ();
        panel1.add (txtRemote, new GridConstraints (5, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension (150, -1), null, 0, false));
        btnOpen = new JButton ();
        btnOpen.setText ("打开");
        panel1.add (btnOpen, new GridConstraints (6, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final JPanel panel2 = new JPanel ();
        panel2.setLayout (new GridLayoutManager (7, 2, new Insets (0, 0, 0, 0), -1, -1));
        root.add (panel2, new GridConstraints (1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
        panel2.setBorder (BorderFactory.createTitledBorder (BorderFactory.createEtchedBorder (), "接收区设置"));
        cbRedirect = new JCheckBox ();
        cbRedirect.setText ("接受转向文件...");
        panel2.add (cbRedirect, new GridConstraints (0, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        cbReturn = new JCheckBox ();
        cbReturn.setText ("自动换行");
        panel2.add (cbReturn, new GridConstraints (1, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        cbShowTime = new JCheckBox ();
        cbShowTime.setText ("显示接收时间");
        panel2.add (cbShowTime, new GridConstraints (2, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        cbShowHex = new JCheckBox ();
        cbShowHex.setText ("十六进制显示");
        panel2.add (cbShowHex, new GridConstraints (3, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        cbPauseReceive = new JCheckBox ();
        cbPauseReceive.setText ("暂停接收显示");
        panel2.add (cbPauseReceive, new GridConstraints (4, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        btnClear = new JButton ();
        btnClear.setText ("清除接收");
        panel2.add (btnClear, new GridConstraints (6, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        btnSave = new JButton ();
        btnSave.setText ("保存...");
        panel2.add (btnSave, new GridConstraints (6, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        cbEcho = new JCheckBox ();
        cbEcho.setEnabled (false);
        cbEcho.setText ("自动回显");
        panel2.add (cbEcho, new GridConstraints (5, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final JPanel panel3 = new JPanel ();
        panel3.setLayout (new GridLayoutManager (5, 3, new Insets (0, 0, 0, 0), -1, -1));
        root.add (panel3, new GridConstraints (4, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
        panel3.setBorder (BorderFactory.createTitledBorder (BorderFactory.createEtchedBorder (), "发送器设置"));
        cbSendFile = new JButton ();
        cbSendFile.setText ("发送文件内容...");
        panel3.add (cbSendFile, new GridConstraints (0, 0, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final Spacer spacer1 = new Spacer ();
        panel3.add (spacer1, new GridConstraints (4, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
        btnAppend = new JCheckBox ();
        btnAppend.setText ("自动发送附加位");
        panel3.add (btnAppend, new GridConstraints (1, 0, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        cbSendHex = new JCheckBox ();
        cbSendHex.setText ("按十六进制发送");
        panel3.add (cbSendHex, new GridConstraints (2, 0, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        cbAutoSend = new JCheckBox ();
        cbAutoSend.setText ("自动发送");
        panel3.add (cbAutoSend, new GridConstraints (3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        txtInterval = new JTextField ();
        txtInterval.setText ("1000");
        panel3.add (txtInterval, new GridConstraints (3, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension (50, -1), null, 0, false));
        final JLabel label3 = new JLabel ();
        label3.setText ("ms");
        panel3.add (label3, new GridConstraints (3, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final JPanel panel4 = new JPanel ();
        panel4.setLayout (new GridLayoutManager (1, 1, new Insets (0, 0, 0, 0), -1, -1));
        root.add (panel4, new GridConstraints (0, 1, 3, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
        panel4.setBorder (BorderFactory.createTitledBorder (BorderFactory.createEtchedBorder (), "接收区"));
        final JScrollPane scrollPane1 = new JScrollPane ();
        panel4.add (scrollPane1, new GridConstraints (0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
        txtReceive = new JTextPane ();
        txtReceive.setText ("");
        scrollPane1.setViewportView (txtReceive);
        final JPanel panel5 = new JPanel ();
        panel5.setLayout (new GridLayoutManager (3, 2, new Insets (0, 0, 0, 0), -1, -1));
        root.add (panel5, new GridConstraints (4, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
        panel5.setBorder (BorderFactory.createTitledBorder (BorderFactory.createEtchedBorder (), "发送区"));
        final JScrollPane scrollPane2 = new JScrollPane ();
        panel5.add (scrollPane2, new GridConstraints (0, 0, 2, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
        txtSend = new JTextArea ();
        scrollPane2.setViewportView (txtSend);
        btnSend = new JButton ();
        btnSend.setText ("发送");
        panel5.add (btnSend, new GridConstraints (0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final Spacer spacer2 = new Spacer ();
        panel5.add (spacer2, new GridConstraints (1, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
        final Spacer spacer3 = new Spacer ();
        panel5.add (spacer3, new GridConstraints (2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false));
        final Spacer spacer4 = new Spacer ();
        root.add (spacer4, new GridConstraints (0, 2, 3, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
        final Spacer spacer5 = new Spacer ();
        root.add (spacer5, new GridConstraints (6, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false));
        pClientList = new JPanel ();
        pClientList.setLayout (new GridLayoutManager (2, 3, new Insets (0, 0, 0, 0), -1, -1));
        root.add (pClientList, new GridConstraints (3, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
        final JLabel label4 = new JLabel ();
        label4.setText ("客户端");
        pClientList.add (label4, new GridConstraints (1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        cbClients = new JComboBox ();
        pClientList.add (cbClients, new GridConstraints (1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        btnClose = new JButton ();
        btnClose.setText ("断开");
        pClientList.add (btnClose, new GridConstraints (1, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final Spacer spacer6 = new Spacer ();
        pClientList.add (spacer6, new GridConstraints (0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false));
        final Spacer spacer7 = new Spacer ();
        root.add (spacer7, new GridConstraints (2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
        final JPanel panel6 = new JPanel ();
        panel6.setLayout (new GridLayoutManager (2, 2, new Insets (0, 0, 0, 0), -1, -1));
        root.add (panel6, new GridConstraints (5, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
        lblLink = new JLabel ();
        lblLink.setText ("https://gitee.com/seth_yang/dreamwork/tree/master/modules/assistant");
        panel6.add (lblLink, new GridConstraints (1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final JLabel label5 = new JLabel ();
        label5.setText ("源码下载");
        panel6.add (label5, new GridConstraints (1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final Spacer spacer8 = new Spacer ();
        panel6.add (spacer8, new GridConstraints (0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false));
    }

    /**
     * @noinspection ALL
     */
    public JComponent $$$getRootComponent$$$ () {
        return root;
    }

    static class Host {
        String host;
        int port;
    }

    static class MessageSendWorker implements Runnable {
        long interval = 1000;
        final Object LOCKER = new byte[0];
        boolean running = true, pause = true;
        byte[] data;
        INetworkAdapter adapter;

        void resume () {
            pause = false;
            synchronized (LOCKER) {
                LOCKER.notifyAll ();
            }
        }

        @Override
        public void run () {
            while (running) {
                synchronized (LOCKER) {
                    while (pause) {
                        try {
                            LOCKER.wait ();
                        } catch (InterruptedException e) {
                            e.printStackTrace ();
                        }
                    }
                }

                if (data != null && adapter != null) {
                    try {
                        adapter.write (data);
                    } catch (IOException e) {
                        e.printStackTrace ();
                    }
                }

                try {
                    Thread.sleep (interval);
                } catch (InterruptedException e) {
                    e.printStackTrace ();
                }
            }
        }
    }

    private static final int SHOW_MSG = -2;
    private static final int SHOW_ERROR = -1;
    private static final int CONNECT = 0;
    private static final int SEND_FILE = 1;
    private static final int SEND_DATA = 2;
    private static final int SAVE_DATA = 3;
}